#include "Matrix4.h"

#include <Rz/Math.h>

namespace Rz { namespace Graphics { namespace Math {

using namespace Rz::Math;

Matrix4::Matrix4() :
	M11(1.0f), M21(0.0f), M31(0.0f), M41(0.0f),
	M12(0.0f), M22(1.0f), M32(0.0f), M42(0.0f),
	M13(0.0f), M23(0.0f), M33(1.0f), M43(0.0f),
	M14(0.0f), M24(0.0f), M34(0.0f), M44(1.0f)
{

}

Matrix4::Matrix4(const Matrix4& m)
{
	// Memory::Copy(Cell, m.Cell, sizeof(Cell));
	M11 = m.M11; M21 = m.M21; M31 = m.M31; M41 = m.M41;
	M12 = m.M12; M22 = m.M22; M32 = m.M32; M42 = m.M42;
	M13 = m.M13; M23 = m.M23; M33 = m.M33; M43 = m.M43;
	M14 = m.M14; M24 = m.M24; M34 = m.M34; M44 = m.M44;
}

Matrix4::Matrix4(f32 m11, f32 m21, f32 m31, f32 m41,
		f32 m12, f32 m22, f32 m32, f32 m42,
		f32 m13, f32 m23, f32 m33, f32 m43,
		f32 m14, f32 m24, f32 m34, f32 m44) :
	M11(m11), M21(m21), M31(m31), M41(m41),
	M12(m12), M22(m22), M32(m32), M42(m42),
	M13(m13), M23(m23), M33(m33), M43(m43),
	M14(m14), M24(m24), M34(m34), M44(m44)
{

}

Matrix4::Matrix4(f32 m[4][4])
{
	M11 = m[0][0]; M21 = m[1][0]; M31 = m[2][0]; M41 = m[3][0];
	M12 = m[0][1]; M22 = m[1][1]; M32 = m[2][1]; M42 = m[3][1];
	M13 = m[0][2]; M23 = m[1][2]; M33 = m[2][2]; M43 = m[3][2];
	M14 = m[0][3]; M24 = m[1][3]; M34 = m[2][3]; M44 = m[3][3];
}

Matrix4& Matrix4::operator = (const Matrix4& m)
{
	// Memory::Copy(Cell, m.Cell, sizeof(Cell));
	M11 = m.M11; M21 = m.M21; M31 = m.M31; M41 = m.M41;
	M12 = m.M12; M22 = m.M22; M32 = m.M32; M42 = m.M42;
	M13 = m.M13; M23 = m.M23; M33 = m.M33; M43 = m.M43;
	M14 = m.M14; M24 = m.M24; M34 = m.M34; M44 = m.M44;

	return *this;
}

Matrix4& Matrix4::operator = (f32 m[4][4])
{
	M11 = m[0][0]; M21 = m[1][0]; M31 = m[2][0]; M41 = m[3][0];
	M12 = m[0][1]; M22 = m[1][1]; M32 = m[2][1]; M42 = m[3][1];
	M13 = m[0][2]; M23 = m[1][2]; M33 = m[2][2]; M43 = m[3][2];
	M14 = m[0][3]; M24 = m[1][3]; M34 = m[2][3]; M44 = m[3][3];

	return (*this);
}

bool Matrix4::operator != (const Matrix4& m) const
{
	return !!Memory::Compare(Cell, m.Cell, sizeof(Cell));
}

Vector3 Matrix4::operator * (const Vector3& v) const
{
	return Vector3(
		Cell[ 0] * v.X + Cell[ 4] * v.Y + Cell[ 8] * v.Z,
		Cell[ 1] * v.X + Cell[ 5] * v.Y + Cell[ 9] * v.Z,
		Cell[ 2] * v.X + Cell[ 6] * v.Y + Cell[10] * v.Z);
}

Vector4 Matrix4::operator * (const Vector4& v) const
{
	return Vector4(
		Cell[ 0] * v.X + Cell[ 4] * v.Y + Cell[ 8] * v.Z + Cell[12] * v.W,
		Cell[ 1] * v.X + Cell[ 5] * v.Y + Cell[ 9] * v.Z + Cell[13] * v.W,
		Cell[ 2] * v.X + Cell[ 6] * v.Y + Cell[10] * v.Z + Cell[14] * v.W,
		Cell[ 3] * v.X + Cell[ 7] * v.Y + Cell[11] * v.Z + Cell[15] * v.W);
}

Matrix4 Matrix4::operator * (const Matrix4& m) const
{				
	Matrix4 result;

	result.M11 = (((M11 * m.M11) + (M12 * m.M21)) + (M13 * m.M31)) + (M14 * m.M41);
	result.M12 = (((M11 * m.M12) + (M12 * m.M22)) + (M13 * m.M32)) + (M14 * m.M42);
	result.M13 = (((M11 * m.M13) + (M12 * m.M23)) + (M13 * m.M33)) + (M14 * m.M43);
	result.M14 = (((M11 * m.M14) + (M12 * m.M24)) + (M13 * m.M34)) + (M14 * m.M44);
	result.M21 = (((M21 * m.M11) + (M22 * m.M21)) + (M23 * m.M31)) + (M24 * m.M41);
	result.M22 = (((M21 * m.M12) + (M22 * m.M22)) + (M23 * m.M32)) + (M24 * m.M42);
	result.M23 = (((M21 * m.M13) + (M22 * m.M23)) + (M23 * m.M33)) + (M24 * m.M43);
	result.M24 = (((M21 * m.M14) + (M22 * m.M24)) + (M23 * m.M34)) + (M24 * m.M44);
	result.M31 = (((M31 * m.M11) + (M32 * m.M21)) + (M33 * m.M31)) + (M34 * m.M41);
	result.M32 = (((M31 * m.M12) + (M32 * m.M22)) + (M33 * m.M32)) + (M34 * m.M42);
	result.M33 = (((M31 * m.M13) + (M32 * m.M23)) + (M33 * m.M33)) + (M34 * m.M43);
	result.M34 = (((M31 * m.M14) + (M32 * m.M24)) + (M33 * m.M34)) + (M34 * m.M44);
	result.M41 = (((M41 * m.M11) + (M42 * m.M21)) + (M43 * m.M31)) + (M44 * m.M41);
	result.M42 = (((M41 * m.M12) + (M42 * m.M22)) + (M43 * m.M32)) + (M44 * m.M42);
	result.M43 = (((M41 * m.M13) + (M42 * m.M23)) + (M43 * m.M33)) + (M44 * m.M43);
	result.M44 = (((M41 * m.M14) + (M42 * m.M24)) + (M43 * m.M34)) + (M44 * m.M44);

	return result;
}

Matrix4& Matrix4::operator *= (const Matrix4& m)
{
	(*this) = (*this) * m;
	return (*this);
}

Matrix4 Matrix4::operator / (f32 n) const
{
	Matrix4 result;

	for (u32 i = 0; i < 16; i++)
		result.Cell[i] = Cell[i] / n;

	return result;
}

Matrix4& Matrix4::operator /= (f32 n)
{
	(*this) = (*this) / n;
	return (*this);
}

bool operator == (const Matrix4& l, const Matrix4& r)
{
	return !Memory::Compare(l.Cell, r.Cell, sizeof(r.Cell));
}

void Matrix4::SetIdentity()
{
	(*this) = Identity;
}

Matrix4& Matrix4::Invert()
{
	Matrix4 inverted;

	inverted.Cell[0]  =  Cell[5] *Cell[10]*Cell[15] -
							Cell[5] *Cell[11]*Cell[14] -
							Cell[9] *Cell[6] *Cell[15] +
							Cell[9] *Cell[7] *Cell[14] +
							Cell[13]*Cell[6] *Cell[11] -
							Cell[13]*Cell[7] *Cell[10];

	inverted.Cell[1]  = -Cell[1] *Cell[10]*Cell[15] +
							Cell[1] *Cell[11]*Cell[14] +
							Cell[9] *Cell[2] *Cell[15] -
							Cell[9] *Cell[3] *Cell[14] -
							Cell[13]*Cell[2] *Cell[11] +
							Cell[13]*Cell[3] *Cell[10];

	inverted.Cell[2]  =  Cell[1] *Cell[6] *Cell[15] -
							Cell[1] *Cell[7] *Cell[14] -
							Cell[5] *Cell[2] *Cell[15] +
							Cell[5] *Cell[3] *Cell[14] +
							Cell[13]*Cell[2] *Cell[7]  -
							Cell[13]*Cell[3] *Cell[6];

	inverted.Cell[3]  = -Cell[1] *Cell[6] *Cell[11] +
							Cell[1] *Cell[7] *Cell[10] +
							Cell[5] *Cell[2] *Cell[11] -
							Cell[5] *Cell[3] *Cell[10] -
							Cell[9] *Cell[2] *Cell[7]  +
							Cell[9] *Cell[3] *Cell[6];

	inverted.Cell[4]  = -Cell[4] *Cell[10]*Cell[15] +
							Cell[4] *Cell[11]*Cell[14] +
							Cell[8] *Cell[6] *Cell[15] -
							Cell[8] *Cell[7] *Cell[14] -
							Cell[12]*Cell[6] *Cell[11] +
							Cell[12]*Cell[7] *Cell[10];

	inverted.Cell[5]  =  Cell[0] *Cell[10]*Cell[15] -
							Cell[0] *Cell[11]*Cell[14] -
							Cell[8] *Cell[2] *Cell[15] +
							Cell[8] *Cell[3] *Cell[14] +
							Cell[12]*Cell[2] *Cell[11] -
							Cell[12]*Cell[3] *Cell[10];

	inverted.Cell[6]  = -Cell[0] *Cell[6] *Cell[15] +
							Cell[0] *Cell[7] *Cell[14] +
							Cell[4] *Cell[2] *Cell[15] -
							Cell[4] *Cell[3] *Cell[14] -
							Cell[12]*Cell[2] *Cell[7]  +
							Cell[12]*Cell[3] *Cell[6];

	inverted.Cell[7]  =  Cell[0] *Cell[6] *Cell[11] -
							Cell[0] *Cell[7] *Cell[10] -
							Cell[4] *Cell[2] *Cell[11] +
							Cell[4] *Cell[3] *Cell[10] +
							Cell[8] *Cell[2] *Cell[7]  -
							Cell[8] *Cell[3] *Cell[6];

	inverted.Cell[8]  =  Cell[4] *Cell[9] *Cell[15] -
							Cell[4] *Cell[11]*Cell[13] -
							Cell[8] *Cell[5] *Cell[15] +
							Cell[8] *Cell[7] *Cell[13] +
							Cell[12]*Cell[5] *Cell[11] -
							Cell[12]*Cell[7] *Cell[9];

	inverted.Cell[9]  = -Cell[0] *Cell[9] *Cell[15] +
							Cell[0] *Cell[11]*Cell[13] +
							Cell[8] *Cell[1] *Cell[15] -
							Cell[8] *Cell[3] *Cell[13] -
							Cell[12]*Cell[1] *Cell[11] +
							Cell[12]*Cell[3] *Cell[9];

	inverted.Cell[10] =  Cell[0] *Cell[5] *Cell[15] -
							Cell[0] *Cell[7] *Cell[13] -
							Cell[4] *Cell[1] *Cell[15] +
							Cell[4] *Cell[3] *Cell[13] +
							Cell[12]*Cell[1] *Cell[7]  -
							Cell[12]*Cell[3] *Cell[5];

	inverted.Cell[11] = -Cell[0] *Cell[5] *Cell[11] +
							Cell[0] *Cell[7] *Cell[9]  +
							Cell[4] *Cell[1] *Cell[11]  -
							Cell[4] *Cell[3] *Cell[9]   -
							Cell[8] *Cell[1] *Cell[7]  +
							Cell[8] *Cell[3] *Cell[5];

	inverted.Cell[12] = -Cell[4] *Cell[9] *Cell[14] +
							Cell[4] *Cell[10]*Cell[13] +
							Cell[8] *Cell[5] *Cell[14] -
							Cell[8] *Cell[6] *Cell[13] -
							Cell[12]*Cell[5] *Cell[10] +
							Cell[12]*Cell[6] *Cell[9];

	inverted.Cell[13] =  Cell[0] *Cell[9] *Cell[14] -
							Cell[0] *Cell[10]*Cell[13] -
							Cell[8] *Cell[1] *Cell[14] +
							Cell[8] *Cell[2] *Cell[13] +
							Cell[12]*Cell[1] *Cell[10] -
							Cell[12]*Cell[2] *Cell[9];

	inverted.Cell[14] = -Cell[0] *Cell[5] *Cell[14] +
							Cell[0] *Cell[6] *Cell[13] +
							Cell[4] *Cell[1] *Cell[14] -
							Cell[4] *Cell[2] *Cell[13] -
							Cell[12]*Cell[1] *Cell[6]  +
							Cell[12]*Cell[2] *Cell[5];

	inverted.Cell[15] =  Cell[0] *Cell[5] *Cell[10] -
							Cell[0] *Cell[6] *Cell[9]  -
							Cell[4] *Cell[1] *Cell[10] +
							Cell[4] *Cell[2] *Cell[9]  +
							Cell[8] *Cell[1] *Cell[6]  -
							Cell[8] *Cell[2] *Cell[5];

	f32 det =	Cell[0]*inverted.Cell[0] +
				Cell[1]*inverted.Cell[4] +
				Cell[2]*inverted.Cell[8] +
				Cell[3]*inverted.Cell[12];

	if (det == 0.0f)
		return (*this);

	det = 1.0f / det;

	for (u32 i = 0; i < 16; i++)
		Cell[i] = inverted.Cell[i] * det;

	return (*this);
}

Matrix4 Matrix4::Inverted() const
{
	Matrix4 m = (*this);
	m.Invert();
	return m;
}

Matrix4& Matrix4::Transpose()
{
	for (u32 x = 0, y;  x < 4; x++)
	{
		for (y = x + 1; y < 4; y++)
		{
			Swap(Cell[y * 4 + x], Cell[x * 4 + y]);
		}
	}

	return *this;
}

Matrix4 Matrix4::Transposed() const
{
	Matrix4 m = (*this);
	m.Transpose();
	return m;
}

void Matrix4::RotateX(f32 angle)
{
	angle = Math::DegToRad(angle);
	f32 p, _sin, _cos;

	_sin = Math::Sin(-angle);
	_cos = Math::Cos(-angle);

	p = Cell[ 4];
	Cell[ 4] = p * _cos - Cell[ 8] * _sin;
	Cell[ 8] = p * _sin + Cell[ 8] * _cos;

	p = Cell[ 5];
	Cell[ 5] = p * _cos - Cell[ 9] * _sin;
	Cell[ 9] = p * _sin + Cell[ 9] * _cos;

	p = Cell[ 6];
	Cell[ 6] = p * _cos - Cell[10] * _sin;
	Cell[10] = p * _sin + Cell[10] * _cos;
}

void Matrix4::RotateY(f32 angle)
{
	angle = Math::DegToRad(angle);
	f32 p, _sin, _cos;

	_sin = Math::Sin(-angle);
	_cos = Math::Cos(-angle);

	p = Cell[ 0];
	Cell[ 0] = p * _cos - Cell[ 8] * _sin;
	Cell[ 8] = p * _sin + Cell[ 8] * _cos;

	p = Cell[ 1];
	Cell[ 1] = p * _cos - Cell[ 9] * _sin;
	Cell[ 9] = p * _sin + Cell[ 9] * _cos;

	p = Cell[ 2];
	Cell[ 2] = p * _cos - Cell[10] * _sin;
	Cell[10] = p * _sin + Cell[10] * _cos;
}

void Matrix4::RotateZ(f32 angle)
{
	angle = Math::DegToRad(angle);
	f32 p, _sin, _cos;

	_sin = Math::Sin(-angle);
	_cos = Math::Cos(-angle);

	p = Cell[ 0];
	Cell[ 0] = p * _cos - Cell[ 4] * _sin;
	Cell[ 4] = p * _sin + Cell[ 4] * _cos;

	p = Cell[ 1];
	Cell[ 1] = p * _cos - Cell[ 5] * _sin;
	Cell[ 5] = p * _sin + Cell[ 5] * _cos;

	p = Cell[ 2];
	Cell[ 2] = p * _cos - Cell[ 6] * _sin;
	Cell[ 6] = p * _sin + Cell[ 6] * _cos;
}

void Matrix4::Rotate(f32 x, f32 y, f32 z)
{
	RotateX(x);
	RotateY(y);
	RotateZ(z);
}

void Matrix4::Rotate(const Vector3& v)
{
	RotateX(v.X);
	RotateY(v.Y);
	RotateZ(v.Z);
}

void Matrix4::Translate(f32 x, f32 y, f32 z)
{
	f32 tmp[3];

	tmp[0] = Cell[12];
	tmp[1] = Cell[13];
	tmp[2] = Cell[14];

	for (u32 i = 0; i < 3; i++)
	{
		tmp[i] += (x * Cell[i] + y * Cell[i + 4] + z * Cell[i + 8]);
	}

	Cell[12] = tmp[0];
	Cell[13] = tmp[1];
	Cell[14] = tmp[2];
}

void Matrix4::Translate(const Vector3& v)
{
	Translate(v.X, v.Y, v.Z);
}

void Matrix4::Scale(f32 x, f32 y, f32 z)
{
	f32 tmp[3];

	tmp[0] = x;
	tmp[1] = y;
	tmp[2] = z;

	for (u32 i = 0; i < 3; i++)
	{
		for (u32 j = 0; j < 3; j++)
		{
			Cell[i * 4 + j] = Cell[j + 4 * i] * tmp[i];
		}
	}
}

void Matrix4::Scale(const Vector3& v)
{
	Scale(v.X, v.Y, v.Z);
}

void Matrix4::Scale(f32 scale)
{
	Scale(scale, scale, scale);
}

void Matrix4::LookAt(const Vector3& position, const Vector3& target, const Vector3& up)
{
	Vector3 zaxis = position - target;
	zaxis.Normalize();

	Vector3 xaxis = Vector3::Cross(up, zaxis);
	xaxis.Normalize();

	Vector3 yaxis = Vector3::Cross(zaxis, xaxis);

	Cell[0] = xaxis.X;
	Cell[1] = yaxis.X;
	Cell[2] = zaxis.X;
	Cell[3] = 0.0f;

	Cell[4] = xaxis.Y;
	Cell[5] = yaxis.Y;
	Cell[6] = zaxis.Y;
	Cell[7] = 0.0f;

	Cell[8] = xaxis.Z;
	Cell[9] = yaxis.Z;
	Cell[10] = zaxis.Z;
	Cell[11] = 0.0f;

	Cell[12] = 0.0f;
	Cell[13] = 0.0f;
	Cell[14] = 0.0f;
	Cell[15] = 1.0f;

	Vector3 rotPos = (*this) * position;

	Cell[12] = -rotPos.X;
	Cell[13] = -rotPos.Y;
	Cell[14] = -rotPos.Z;
	Cell[15] = 1.0f;
}

void Matrix4::LookAt(f32 posX, f32 posY, f32 posZ,
			f32 tarX, f32 tarY, f32 tarZ,
			f32 upX, f32 upY, f32 upZ)
{
	LookAt(Vector3(posX, posY, posZ),
			Vector3(tarX, tarY, tarZ),
			Vector3(upX, upY, upZ));
}

Vector3 Matrix4::GetTranslation() const
{
	return Vector3(Cell[12], Cell[13], Cell[14]);
}

Vector4 Matrix4::GetRow(u32 row)
{
	return Vector4(Cell[row * 4 + 0], Cell[row * 4 + 1], Cell[row * 4 + 2], Cell[row * 4 + 3]);
}

Vector4 Matrix4::GetColumn(u32 column)
{
	return Vector4(Cell[column], Cell[column + 4], Cell[column + 8], Cell[column + 12]);
}

f32 Matrix4::GetCell(u32 index) const
{
	return Cell[index];
}

f32 Matrix4::GetCell(u32 x, u32 y) const
{
	return Cell[x * 4 + y];
}

void Matrix4::SetCell(u32 index, f32 value)
{
	Cell[index] = value;
}

void Matrix4::SetCell(u32 x, u32 y, f32 value)
{
	Cell[x * 4 + y] = value;
}

Matrix3 Matrix4::ToMatrix3() const
{
	return Matrix3(	M11, M21, M31,
					M12, M22, M32,
					M13, M23, M33);
}

//////////////////////////////////////////////////////////////////////////
// IHumanReadable
String Matrix4::ToString() const
{
	return (
		String::Format("[%.3f %.3f %.3f %.3f]\n",   M11, M21, M31, M41) +
		String::Format("|%.3f %.3f %.3f %.3f|\n",   M12, M22, M32, M42) +
		String::Format("|%.3f %.3f %.3f %.3f|\n",   M13, M23, M33, M43) +
		String::Format("[%.3f %.3f %.3f %.3f]"  , M14, M24, M34, M44)
		);
}

//////////////////////////////////////////////////////////////////////////
// static
Matrix4 Matrix4::CreateRotationX(f32 angle)
{
	Matrix4 result = Identity;
	result.RotateX(angle);
	return result;
}

Matrix4 Matrix4::CreateRotationY(f32 angle)
{
	Matrix4 result = Identity;
	result.RotateY(angle);
	return result;
}

Matrix4 Matrix4::CreateRotationZ(f32 angle)
{
	Matrix4 result = Identity;
	result.RotateZ(angle);
	return result;
}

Matrix4 Matrix4::CreateRotation(f32 x, f32 y, f32 z)
{
	Matrix4 result = Identity;
	result.Rotate(x, y, z);
	return result;
}

Matrix4 Matrix4::CreateRotation(const Vector3& v)
{
	Matrix4 result = Identity;
	result.Rotate(v);
	return result;
}

Matrix4 Matrix4::CreateTranslation(f32 x, f32 y, f32 z)
{
	Matrix4 result = Identity;
	result.Translate(x, y, z);
	return result;
}

Matrix4 Matrix4::CreateTranslation(const Vector3& v)
{
	Matrix4 result = Identity;
	result.Translate(v);
	return result;
}

Matrix4 Matrix4::CreateScale(f32 x, f32 y, f32 z)
{
	Matrix4 result = Identity;
	result.Scale(x, y, z);
	return result;
}

Matrix4 Matrix4::CreateScale(const Vector3& v)
{
	Matrix4 result = Identity;
	result.Scale(v);
	return result;
}

Matrix4 Matrix4::CreateScale(f32 scale)
{
	Matrix4 result = Identity;
	result.Scale(scale, scale, scale);
	return result;
}

Matrix4 Matrix4::CreateLookAt(const Vector3& position, const Vector3& target, const Vector3& up)
{
	Matrix4 result;
	result.LookAt(position, target, up);
	return result;
}

Matrix4 Matrix4::CreateLookAt(f32 posX, f32 posY, f32 posZ,
							f32 tarX, f32 tarY, f32 tarZ,
							f32 upX, f32 upY, f32 upZ)
{
	Matrix4 result;
	result.LookAt(	Vector3(posX, posY, posZ),
					Vector3(tarX, tarY, tarZ),
					Vector3(upX, upY, upZ));
	return result;
}

Matrix4 Matrix4::CreateInverseLookAt(const Vector3& position, const Vector3& target, const Vector3& up, f32 planeY)
{
	return CreateInverseLookAt(position.X, position.Y, position.Z, target.X, target.Y, target.Z, up.X, up.Y, up.Z, planeY);
}

Matrix4 Matrix4::CreateInverseLookAt(f32 posX, f32 posY, f32 posZ,
											f32 tarX, f32 tarY, f32 tarZ,
											f32 upX, f32 upY, f32 upZ, f32 planeY)
{
	Matrix4 result;

	result.LookAt(	Vector3(posX, 2.0f*planeY - posY, posZ),
					Vector3(tarX, 2.0f*planeY - tarY, tarZ),
					Vector3(-upX, -upY, -upZ));

	return CreateScale(-1.0f, 1.0f, 1.0f) * result;
}

Matrix4 Matrix4::CreateOrthographic(f32 width, f32 height, f32 nearPlane, f32 farPlane)
{
	f32	depth  = farPlane - nearPlane,

			a = 2.0f / width,
			b = 2.0f / height,
			c = -2.0f / depth,
			z = -(farPlane + nearPlane) / depth;

	return Matrix4(	a,		0.0f,	0.0f,	0.0f,
					0.0f,	b,		0.0f,	0.0f,
					0.0f,	0.0f,	c,		z,
					0.0f,	0.0f,	0.0f,	1.0f);
}

Matrix4 Matrix4::CreateFrustum(f32 left, f32 right, f32 bottom, f32 top, f32 nearVal, f32 farVal)
{
	f32	a = (right + left) / (right - left),
		b = (top + bottom) / (top - bottom),
		c = -(farVal + nearVal) / (farVal - nearVal),
		d = -(2.0f * farVal * nearVal) / (farVal - nearVal),
		x = (2.0f * nearVal) / (right - left),
		y = (2.0f * nearVal) / (top - bottom);

			
	return Matrix4(	x,		0.0f,	0.0f,	0.0f,
					0.0f,	y,		0.0f,	0.0f,
					a,		b,		c,		-1.0f,
					0.0f,	0.0f,	d,		0.0f);

}

Matrix4 Matrix4::CreatePerspectiveFieldOfView(f32 fieldOfView, f32 aspectRatio, f32 nearPlane, f32 farPlane)
{
	f32 minX, maxX, minY, maxY;

	// This is the FOV / 2 converted to radians
	// and then multiplied by the near value -
	// gives us our max y position at the front
	// of the frustum. The min is just the opposite
	maxY = nearPlane * Math::Tan(fieldOfView * (f32)Math::Pi / 360.0f);
	minY = -maxY;

	// the min and max x are then the same as
	// the min and max y multiplied by the
	// aspect ratio
	minX = minY * aspectRatio;
	maxX = maxY * aspectRatio;

	return CreateFrustum(minX, maxX, minY, maxY, nearPlane, farPlane);
}

//////////////////////////////////////////////////////////////////////////
// constants
const Matrix4 Matrix4::Zero     = Matrix4(0.0f, 0.0f, 0.0f, 0.0f,
										  0.0f, 0.0f, 0.0f, 0.0f,
										  0.0f, 0.0f, 0.0f, 0.0f,
										  0.0f, 0.0f, 0.0f, 0.0f);

const Matrix4 Matrix4::One      = Matrix4(1.0f, 1.0f, 1.0f, 1.0f,
										  1.0f, 1.0f, 1.0f, 1.0f,
									      1.0f, 1.0f, 1.0f, 1.0f,
									      1.0f, 1.0f, 1.0f, 1.0f);

const Matrix4 Matrix4::Identity = Matrix4(1.0f, 0.0f, 0.0f, 0.0f,
									      0.0f, 1.0f, 0.0f, 0.0f,
									      0.0f, 0.0f, 1.0f, 0.0f,
									      0.0f, 0.0f, 0.0f, 1.0f);

} } }
